﻿using System.ServiceModel;
using NUnit.Framework;

namespace Universe.WCF.Behaviors.Test
{
    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Threading;
    using System.Threading.Tasks;


    [TestFixture]
    public class AllTests : BaseTest
    {
        [TestFixtureSetUp]
        public void TestFixtureSetUp()
        {
            CrossInfo.AttachUnitTrace("WCF Tests. Basic");
        }

        [SetUp]
        public void SetUp()
        {
            MyEnvionment.ResetTcpPort();
            CrossInfo.NextTest();
        }

        [Test]
        public void Test_1_Pure_Connection()
        {
            for (int iteration = 1; iteration <= 5; iteration++)
            {
                MyEnvionment.ResetTcpPort(); 
                Trace.WriteLine("  Iteration: " + iteration);
                Test_Pure_Connection_Iteration(iteration);
            }

        }

        private static void Test_Pure_Connection_Iteration(int iteration)
        {
            Stopwatch startAt = Stopwatch.StartNew();
            ServiceHost host = MyEnvionment.CreateService<IService1<int>, Service1<int>>();
            {

                if (iteration == 1) Trace.WriteLine("  Host is configured");
                host.Open();
                if (iteration == 1) Trace.WriteLine("  Host is opened");

                using (ChannelFactory<IService1<int>> channel = MyEnvionment.CreateProxy<IService1<int>>())
                {

                    if (iteration == 1) Trace.WriteLine("  Channel is configured");

                    var service1 = channel.CreateChannel(MyEnvionment.GetEndPointUri<IService1<int>>());
                    if (iteration == 1) Trace.WriteLine("  Channel is opened");

                    CompositeType ret = null;
                    try
                    {
                        ret = service1.GetData(
                            new CompositeType() {BoolValue = true, StringValue = "Hello"}
                            );
                    }
                    catch (Exception ex)
                    {
                        Trace.WriteLine("  !Call failed :(" + Environment.NewLine + ex);
                    }

                    if (ret != null && iteration == 1)
                        Trace.WriteLine("  String Result is " + ret.StringValue);

                    channel.Close();
                    if (iteration == 1) Trace.WriteLine("  Channel is closed");
                }

                if (!CrossInfo.IsLinuxOnArm)
                {
                    host.Close();
                    if (iteration == 1) Trace.WriteLine("  Host is closed");
                }

                Trace.WriteLine("  Time: " + startAt.Elapsed + Environment.NewLine);
            }

        }

        [Test]
        public void Test_3_Traffic_Behavior()
        {
            TrafficStatistic serverTraffic = new TrafficStatistic();
            var serverConfiguration = new TrafficStatisticConfiguration(
                delegate { },
                serverTraffic.Merge);

            ServiceHost host = MyEnvionment.CreateService<IService1<int>, Service1<int>>();
            // DISPATCHER's CONFIGURATION
            host.Description.Behaviors.Add(new TrafficBehavior(serverConfiguration));
            host.Open();

            TrafficStatistic clientTraffic = new TrafficStatistic();
            var clientConfiguration = new TrafficStatisticConfiguration(
                clientTraffic.Merge,
                delegate { });

            ChannelFactory<IService1<int>> channel = MyEnvionment.CreateProxy<IService1<int>>();
            channel.Endpoint.Behaviors.Add(new TrafficBehavior(clientConfiguration));
            var service1 = channel.CreateChannel(MyEnvionment.GetEndPointUri<IService1<int>>());

            service1.GetData(
                new CompositeType() {BoolValue = true, StringValue = "Hello"}
                );

            channel.Close();

            if (Environment.OSVersion.Platform != PlatformID.Unix)
            {
                host.Close();
            }

            
            Assert.Greater(serverTraffic.Total.Recieved, 0);
            Assert.Greater(serverTraffic.Total.Sent, 0);
            Assert.AreEqual(1, serverTraffic.Total.Requests);
            Trace.WriteLine("  Host traffic: " + serverTraffic.Total);

            Assert.Greater(clientTraffic.Total.Recieved, 0);
            Assert.Greater(clientTraffic.Total.Sent, 0);
            Assert.AreEqual(1, clientTraffic.Total.Requests);
            Trace.WriteLine("  Client's channel traffic: " + clientTraffic.Total);

        }

        [Test]
        public void Test_2_Transparent_Delivery_Behavior()
        {
            for (int iteration = 1; iteration <= 5; iteration++)
            {
                MyEnvionment.ResetTcpPort();
                Trace.WriteLine("  Iteration: " + iteration);
                Test_Transparent_Delivery_Iteration(iteration);
            }
        }

        private static void Test_Transparent_Delivery_Iteration(int iteration)
        {
            Stopwatch startAt = Stopwatch.StartNew();

            int arrivedId = 0;
            string arrivedCulture = null;

            var tdc = new TransparentDeliveryConfiguration(
                delegate(string action, DeliveryDictionary data)
                {
                    data.Set<int>("ID", 321);
                    data.Set<string>("Culture", "en-US");
                },
                delegate(string action, DeliveryDictionary data)
                {
                    arrivedId = data.Get<int>("ID");
                    arrivedCulture = data.Get<string>("Culture");
                });

            ServiceHost host = MyEnvionment.CreateService<IService1<int>, Service1<int>>();
            {
                if (iteration == 1) Trace.WriteLine("  Host Created");
                host.Description.Behaviors.Add(new TransparentDeliveryBehavior(tdc));
                if (iteration == 1) Trace.WriteLine("  Host configured with TransparentDeliveryBehavior");
                host.Open();
                if (iteration == 1) Trace.WriteLine("  Host opened");

                using (ChannelFactory<IService1<int>> channel = MyEnvionment.CreateProxy<IService1<int>>())
                {
                    if (iteration == 1) Trace.WriteLine("  Channel created");

                    // CLIENT CHANNEL's CONFIGURATION
                    channel.Endpoint.Behaviors.Add(new TransparentDeliveryBehavior(tdc));
                    if (iteration == 1) Trace.WriteLine("  Channel configured with TransparentDeliveryBehavior");

                    var service1 = channel.CreateChannel(MyEnvionment.GetEndPointUri<IService1<int>>());
                    if (iteration == 1) Trace.WriteLine("  Channel opened");

                    CompositeType ret = null;
                    try
                    {
                        ret = service1.GetData(
                            new CompositeType() {BoolValue = true, StringValue = "Hello"}
                            );
                    }
                    catch (Exception ex)
                    {
                        Trace.WriteLine("  !Call failed :(" + Environment.NewLine + ex);
                    }

                    if (ret != null && iteration == 1)
                        Trace.WriteLine("  String Result is " + ret.StringValue);


                    channel.Close();
                    if (iteration == 1) Trace.WriteLine("  Channel closed");
                }

                if (!CrossInfo.IsLinuxOnArm)
                {
                    host.Close();
                    if (iteration == 1) Trace.WriteLine("  Host closed");
                }

                Assert.AreEqual(321, arrivedId);
                Assert.AreEqual("en-US", arrivedCulture);

                Trace.WriteLine("  Time: " + startAt.Elapsed + Environment.NewLine);
            }

        }

        [Test]
        public void Test_4_EmptyStress_10secs()
        {
            MultiThread_Empty(10);
        }

        private static void MultiThread_Empty(int duration)
        {

            int total = 0;
            ParallelOptions po = new ParallelOptions() { MaxDegreeOfParallelism = 64 };
            Stopwatch at = Stopwatch.StartNew();
            Dictionary<int, object> threads = new Dictionary<int, object>();
            Parallel.For(1, po.MaxDegreeOfParallelism, po, (index) =>
            {
                while (at.Elapsed <= TimeSpan.FromSeconds(duration))
                {
                    try
                    {
                        Thread.Sleep(0);
                        Interlocked.Increment(ref total);
                        lock (threads) threads[Thread.CurrentThread.ManagedThreadId] = null;
                    }
                    catch (Exception ex)
                    {
                        Trace.WriteLine("PARALLEL op failed" + Environment.NewLine + ex);
                    }
                }
            });

            Trace.WriteLine("  Threads: " + threads.Count + ", operations: " + total);
        }

        [Test]
        public void Test_5_Stress_Pure_Connection_10secs()
        {
            Stress_Pure_Connection(10);
        }

        private static void Stress_Pure_Connection(int duration)
        {
            MyEnvionment.ResetTcpPort();
            ServiceHost host = MyEnvionment.CreateService<IService1<int>, Service1<int>>();
            Trace.WriteLine("  Host is configured");
            host.Open();
            Trace.WriteLine("  Host is opened");

            ChannelFactory<IService1<int>> channel = MyEnvionment.CreateProxy<IService1<int>>();
            Trace.WriteLine("  Channel is configured");

            var service1 = channel.CreateChannel(MyEnvionment.GetEndPointUri<IService1<int>>());
            Trace.WriteLine("  Channel is opened");

            var ret = service1.GetData(
                new CompositeType() {BoolValue = true, StringValue = "Hello"}
                );

            Trace.WriteLine("  Channel is heated");

            int total = 0;
            ParallelOptions po = new ParallelOptions() { MaxDegreeOfParallelism = 64 };
            Stopwatch at = Stopwatch.StartNew();
            Dictionary<int, object> threads = new Dictionary<int, object>();
            Parallel.For(1, po.MaxDegreeOfParallelism, po, (index) =>
            {
                while (at.Elapsed <= TimeSpan.FromSeconds(duration))
                {
                    try
                    {
                        service1.GetData(
                            new CompositeType() { BoolValue = true, StringValue = "Hello" }
                            );

                        Thread.Sleep(0);
                        Interlocked.Increment(ref total);
                        lock (threads) threads[Thread.CurrentThread.ManagedThreadId] = null;
                    }
                    catch (Exception ex)
                    {
                        Trace.WriteLine("PARALLEL op failed" + Environment.NewLine + ex);
                    }
                }
            });

            Trace.WriteLine("  Threads: " + threads.Count + ", calls: " + total);
        }

        [Test]
        public void Test_6_Stress_Traffic_Profiler()
        {
            Stress_Traffic_Profiler(10);
        }

        static void Stress_Traffic_Profiler(int duration)
        {
            MyEnvionment.ResetTcpPort();
            TrafficStatistic serverTraffic = new TrafficStatistic();
            var serverConfiguration = new TrafficStatisticConfiguration(
                delegate { },
                serverTraffic.Merge);

            ServiceHost host = MyEnvionment.CreateService<IService1<int>, Service1<int>>();
            // DISPATCHER's CONFIGURATION
            host.Description.Behaviors.Add(new TrafficBehavior(serverConfiguration));
            host.Open();

            TrafficStatistic clientTraffic = new TrafficStatistic();
            var clientConfiguration = new TrafficStatisticConfiguration(
                clientTraffic.Merge,
                delegate { });

            ChannelFactory<IService1<int>> channel = MyEnvionment.CreateProxy<IService1<int>>();
            channel.Endpoint.Behaviors.Add(new TrafficBehavior(clientConfiguration));
            var service1 = channel.CreateChannel(MyEnvionment.GetEndPointUri<IService1<int>>());

            service1.GetData(
                new CompositeType() { BoolValue = true, StringValue = "Hello" }
                );

            int total = 0;
            ParallelOptions po = new ParallelOptions() { MaxDegreeOfParallelism = 64 };
            Stopwatch at = Stopwatch.StartNew();
            Dictionary<int, object> threads = new Dictionary<int, object>();
            Parallel.For(1, po.MaxDegreeOfParallelism, po, (index) =>
            {
                while (at.Elapsed <= TimeSpan.FromSeconds(duration))
                {
                    try
                    {
                        service1.GetData(
                            new CompositeType() { BoolValue = true, StringValue = "Hello" }
                            );

                        Thread.Sleep(0);
                        Interlocked.Increment(ref total);
                        lock (threads) threads[Thread.CurrentThread.ManagedThreadId] = null;
                    }
                    catch (Exception ex)
                    {
                        Trace.WriteLine("PARALLEL op failed" + Environment.NewLine + ex);
                    }
                }
            });

            Trace.WriteLine("  Threads: " + threads.Count + ", calls: " + total);


        }



    }
}